TechNote - Window Focus
Thursday, June 4, 2026
7:21 AM
This article discusses how to ensure a modal or modeless dialog displayed by the OneMore add-in can reliably demand keyboard focus when it is shown. This is likely a common case across any Office COM add-in and perhaps other situations, so could be instructive.
OneMore dialogs (search, command palette, etc.) may open without receiving keyboard focus — the focus stays on the OneNote page. This happens because Windows enforces strict foreground window security, where calls to SetForegroundWindow() silently fails unless the calling thread currently holds "foreground rights."
OneMore runs as a COM surrogate (dllhost.exe). Both of these trigger paths lose foreground rights before the dialog appears:
- Ribbon path: SearchCmd is async Task — it returns the task to the ribbon framework immediately, and the dialog is created on a ThreadPool continuation thread that has no foreground rights.
- Hotkey path: key.Action() is invoked from WndProc (which does have rights via WM_HOTKEY), but the async method suspends at the first await and continues on a ThreadPool thread — rights are lost.
We originally attempted to solve this with an "creative" Elevate() method that used the common TopMost toggle trick, and calling Select()/Focus(), but these don't actually transfer focus from another process. SetForegroundWindow was commented out because it (didn't work but actually) fails silently without rights.
AttachThreadInput
The well-established technique is to temporarily attach the input queue of our dialog thread to the foreground thread (OneNote), which grants our thread the ability to call SetForegroundWindow successfully:
uint foregroundThread = Native.GetWindowThreadProcessId(foreground, out _);
uint currentThread = Native.GetCurrentThreadId();
Native.AttachThreadInput(foregroundThread, currentThread, true); // attach
Native.SetForegroundWindow(Handle);
Native.BringWindowToTop(Handle);
Native.AttachThreadInput(foregroundThread, currentThread, false); // detach immediately
The detach is immediate — the threads share the input queue only for the two API calls, which avoids any side effects on modal loops or message routing.
#omwiki #omdeveloper #omtechnote
© 2020 Steven M Cohn. All rights reserved.
Please consider a sponsorship or one-time donation to support ongoing development
Created with OneNote.